package com.jie.netty.processor;

import cn.hutool.core.bean.BeanUtil;
import com.jie.constant.ChannelAttrKey;
import com.jie.constant.IMConstant;
import com.jie.constant.IMRedisKey;
import com.jie.enums.IMCmdType;
import com.jie.netty.model.IMSendInfo;
import com.jie.vo.UserLoginProcessVo;
import com.jie.netty.manage.UserChannelCtxMap;
import com.jie.netty.model.IMLoginInfo;
import com.jie.netty.serve.IMServerGroup;
import com.jie.util.JwtTokenUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import static com.jie.constant.UserConstant.USER_INFO;

/**
 * 登录进行channel保存
 */
@Slf4j
@Component
public class LoginProcessor extends AbstractMessageProcessor<IMLoginInfo> {

    @Autowired
    RedisTemplate<String,Object> redisTemplate;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    synchronized public void process(ChannelHandlerContext ctx, IMLoginInfo loginInfo) {
        if (loginInfo == null) {
            return;
        }
        //通过JWT来解析 1.获取jwt
        String authToken = loginInfo.getAccessToken().substring(tokenHead.length());
        //根据authtoken得到用户名
        String username = jwtTokenUtil.getUsernameFromToken(authToken);
        ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue();
        //从缓存中获取
        if (!redisTemplate.hasKey(USER_INFO+":"+username)){
            IMSendInfo sendInfo = new IMSendInfo();
            sendInfo.setCmd(IMCmdType.FORCE_LOGUT.code());
            sendInfo.setData("token失效或username失效");
            log.info("token失效或username失效",IMCmdType.FORCE_LOGUT.code());
            ctx.channel().writeAndFlush(sendInfo);
            return;
        }
        UserLoginProcessVo userDetailDTO= (UserLoginProcessVo) stringObjectValueOperations.get(USER_INFO + ":" + username);
        Long userInfoId =userDetailDTO.getUserInfoId();
        Integer terminal = userDetailDTO.getTerminal();
        log.info("用户登录，userId:{}",userDetailDTO.getUserInfoId());
        ChannelHandlerContext context = UserChannelCtxMap.getChannelCtx(userInfoId,terminal);
        if(context != null && !ctx.channel().id().equals(context.channel().id())){
            // 不允许多地登录,强制下线
            IMSendInfo sendInfo = new IMSendInfo();
            sendInfo.setCmd(IMCmdType.FORCE_LOGUT.code());
            sendInfo.setData("您已在其他地方登陆，将被强制下线");
            log.info("异地登录，强制下线,userId:{}",userInfoId);
            context.channel().writeAndFlush(sendInfo);
        }
        // 绑定用户和channel
        UserChannelCtxMap.addChannelCtx(userInfoId,terminal,ctx);
        // 设置用户id属性
        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
        ctx.channel().attr(userIdAttr).set(userInfoId);
        // 设置用户终端类型
        AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
        ctx.channel().attr(terminalAttr).set(terminal);
        // 初始化心跳次数
        AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf("HEARTBEAt_TIMES");
        ctx.channel().attr(heartBeatAttr).set(0L);
        // 在redis上记录每个user的channelId，15秒没有心跳，则自动过期
        String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID,userInfoId.toString(),terminal.toString());
        stringObjectValueOperations.set(key, IMServerGroup.serverId, IMConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
        // 响应ws
        IMSendInfo sendInfo = new IMSendInfo();
        sendInfo.setCmd(IMCmdType.LOGIN.code());
        sendInfo.setData("登录成功");
        ctx.channel().writeAndFlush(sendInfo);
    }
    @Override
    public IMLoginInfo transForm(Object o) {
        HashMap map = (HashMap)o;
        IMLoginInfo loginInfo = BeanUtil.fillBeanWithMap(map, new IMLoginInfo(), false);
        return  loginInfo;
    }
}
